#include "AVPlayer.h"
#include "log.h"
#pragma warning(disable : 4996)

// Buffer:
static int audio_len = 0;
static Uint8 *audio_pos;

void AVPlayer::audioCallBack(void *userdata, uint8_t *stream, int len) {

    SDL_memset(stream, 0, len);
    if (audio_len <= 0)
        return;

    len = (len > audio_len ? audio_len
                           : len); /*  Mix  as  much  data  as  possible  */

    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    audio_pos += len;
    audio_len -= len;
}

AVPlayer::AVPlayer() { LOGI("AVPlayer::AVPlayer()"); }

AVPlayer::~AVPlayer() {
    LOGI("AVPlayer::~AVPlayer() start");

    read_eof = false;
    ChangeState(AVPlayer::State::IDLE);
    video_index = -1;
    audio_index = -1;

    for (auto &th : work_threads) {
        th.join();
    }

    work_threads.clear();

    ClearVideoPkt();
    ClearAudioPkt();

    if (video_codec_context) {

        avcodec_close(video_codec_context);
        avcodec_free_context(&video_codec_context);
        video_codec_context = NULL;
    }

    if (audio_codec_context) {
        avcodec_close(audio_codec_context);
        avcodec_free_context(&audio_codec_context);
        audio_codec_context = NULL;
    }

    if (format_context) {
        // 拉流不需要释放start
        // if (mFmtCtx && !(mFmtCtx->oformat->flags & AVFMT_NOFILE)) {
        //    avio_close(mFmtCtx->pb);
        //}
        // 拉流不需要释放end
        avformat_close_input(&format_context);
        avformat_free_context(format_context);
        format_context = NULL;
    }

    if (sdl_window) {
        SDL_DestroyWindow(sdl_window);
    }

    if (sdl_renderer) {
        SDL_DestroyRenderer(sdl_renderer);
    }

    if (sdl_texture) {
        SDL_DestroyTexture(sdl_texture);
    }

    SDL_Quit();
    LOGI("AVPlayer::~AVPlayer() end");
}

int AVPlayer::start(const char *input) {
    switch (player_state) {
    case AVPlayer::State::IDLE:
    case AVPlayer::State::STOPPED:
        break;

    case AVPlayer::State::INITED:
        return 0;

    case AVPlayer::State::PLAYING:
        Pause();
        return 0;

    case AVPlayer::State::PAUSED:
        Resume();
        return 0;
    case AVPlayer::State::COMPLETE:
    default:
        return 0;
    }

    format_context = avformat_alloc_context();

    if (avformat_open_input(&format_context, input, nullptr, nullptr) != 0) {
        printf("无法打开视频文件 \n");
        return -1;
    }
    if (avformat_find_stream_info(format_context, nullptr) < 0) {
        printf("无法获取视频流信息 \n");
        return -1;
    }

    video_index = av_find_best_stream(format_context, AVMEDIA_TYPE_VIDEO, -1,
                                      -1, nullptr, 0);
    if (video_index >= 0) {

        AVCodecParameters *videoPar =
            format_context->streams[video_index]->codecpar;
        const AVCodec *videoCodec = avcodec_find_decoder(videoPar->codec_id);

        // 等比例缩放画面，避免拉伸变形
        frame_width = sdl_window_width;
        frame_height = sdl_window_height;
        if (videoPar->height) {
            frame_height =
                sdl_window_width * (1.0 * videoPar->height / videoPar->width);
        }

        if (!videoCodec) {
            printf("视频解码器不存在 \n");
            return -1;
        }
        video_codec_context = avcodec_alloc_context3(videoCodec);
        if (avcodec_parameters_to_context(video_codec_context, videoPar) != 0) {
            printf("视频解码器无法copy上下文 \n");
            return -1;
        }
        if (avcodec_open2(video_codec_context, videoCodec, nullptr) < 0) {
            printf("视频解码器无法打开 \n");
            return -1;
        }

        work_threads.emplace_back(&AVPlayer::HandleVideo, this);

    } else {
        printf("未找到视频流 \n");

        return -1;
    }

    audio_index = av_find_best_stream(format_context, AVMEDIA_TYPE_AUDIO, -1,
                                      -1, nullptr, 0);
    if (audio_index >= 0) {
        AVCodecParameters *audioPar =
            format_context->streams[audio_index]->codecpar;
        const AVCodec *audioCodec = avcodec_find_decoder(audioPar->codec_id);

        if (!audioCodec) {
            printf("音频解码器不存在 \n");
        }

        audio_codec_context = avcodec_alloc_context3(audioCodec);

        if (avcodec_parameters_to_context(audio_codec_context, audioPar) != 0) {
            printf("音频解码器无法copy上下文 \n");
        }

        if (avcodec_open2(audio_codec_context, audioCodec, nullptr) < 0) {
            printf("音频解码器无法打开 \n");
        }

        work_threads.emplace_back(&AVPlayer::HandleAudio, this);

    } else {
        printf("未找到音频流 \n");
    }

    AVStream *videoStream = format_context->streams[video_index];
    int videoFps =
        videoStream->avg_frame_rate.num / videoStream->avg_frame_rate.den;
    LOGI("videoFps=%d", videoFps);

    work_threads.emplace_back(&AVPlayer::Read, this);

    ChangeState(AVPlayer::State::INITED);
    return 0;
}

void AVPlayer::Init(void *winId, int winWidth, int winHeight) {
    sdl_window_width = winWidth;
    sdl_window_height = winHeight;
    initSDL(winId);
}

bool AVPlayer::IsPlaying() { return player_state == AVPlayer::State::PLAYING; }

void AVPlayer::Pause() { ChangeState(AVPlayer::State::PAUSED); }

void AVPlayer::Resume() { ChangeState(AVPlayer::State::PLAYING); }

void AVPlayer::HandleVideo() {

    AVFrame *frame = av_frame_alloc();
    AVFrame *sw_frame = av_frame_alloc();
    sw_frame->format = AV_PIX_FMT_YUV420P;

    sw_frame->width = sdl_window_width;
    sw_frame->height = sdl_window_height;
    int sw_frame_buff_size = av_image_get_buffer_size(
        AV_PIX_FMT_YUV420P, sw_frame->width, sw_frame->height, 1);
    uint8_t *sw_frame_buff = (uint8_t *)av_malloc(sw_frame_buff_size);
    av_image_fill_arrays(sw_frame->data, sw_frame->linesize, sw_frame_buff,
                         AV_PIX_FMT_YUV420P, sw_frame->width, sw_frame->height,
                         1);

    int ret;
    while (true) {
        if (player_state != AVPlayer::State::PLAYING) {
            continue;
        }

        AVPacket pkt;
        if (!PopVideoPacket(pkt)) {
            continue;
        }

        avcodec_send_packet(video_codec_context, &pkt);
        while (true) {
            ret = avcodec_receive_frame(video_codec_context, frame);
            if (ret >= 0) {
                auto img_convert_ctx = sws_getContext(
                    frame->width, frame->height,
                    (enum AVPixelFormat)frame->format, sw_frame->width,
                    sw_frame->height, (enum AVPixelFormat)sw_frame->format,
                    SWS_BICUBIC, NULL, NULL, NULL);

                sws_scale(img_convert_ctx,
                          (const unsigned char *const *)frame->data,
                          frame->linesize, 0, frame->height, sw_frame->data,
                          sw_frame->linesize);
                sws_freeContext(img_convert_ctx);

                SDL_UpdateYUVTexture(sdl_texture, nullptr, sw_frame->data[0],
                                     sw_frame->linesize[0], sw_frame->data[1],
                                     sw_frame->linesize[1], sw_frame->data[2],
                                     sw_frame->linesize[2]);

                SDL_Rect srcRect;
                srcRect.x = 0;
                srcRect.y = 0;
                srcRect.w = sw_frame->width;
                srcRect.h = sw_frame->height;

                SDL_Rect dstRect;
                dstRect.x = 0;
                dstRect.y = (sdl_window_height - frame_height) / 2;
                dstRect.w = frame_width;
                dstRect.h = frame_height;

                SDL_RenderClear(sdl_renderer);
                SDL_RenderCopy(sdl_renderer, sdl_texture, &srcRect, &dstRect);
                SDL_RenderPresent(sdl_renderer);

                av_packet_unref(&pkt);

                std::this_thread::sleep_for(std::chrono::milliseconds(40));

            } else {
                break;
            }
        }
    }

    av_frame_free(&frame);
    frame = nullptr;
    free(sw_frame_buff);
    sw_frame_buff = nullptr;
    av_frame_free(&sw_frame);
    sw_frame = nullptr;
}

void AVPlayer::HandleAudio() {
    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO; // 输出声道
    // out_channel_layout = AV_CH_SIDE_LEFT;
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
    int out_nb_samples = 1024;
    AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; // 输出格式S16
    int out_sample_rate = 44100;

    int out_buffer_size = av_samples_get_buffer_size(
        nullptr, out_channels, out_nb_samples, out_sample_fmt, 1);
    uint8_t *out_buffer = (uint8_t *)av_malloc(out_buffer_size);

    SDL_AudioSpec spec;
    spec.freq = out_sample_rate;
    spec.format = AUDIO_S16SYS;
    spec.channels = out_channels;
    spec.silence = 0;
    spec.samples = out_nb_samples;
    spec.callback = AVPlayer::audioCallBack;
    spec.userdata = this;

    if (SDL_OpenAudio(&spec, nullptr) < 0) {
        LOGE("SDL_OpenAudio error");
        return;
    }

    // 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候，将会播放静音的值
    SDL_PauseAudio(0);

    uint64_t in_channel_layout =
        av_get_default_channel_layout(audio_codec_context->channels);
    AVFrame *in_frame = av_frame_alloc();
    AVSampleFormat in_sample_fmt = audio_codec_context->sample_fmt;
    int in_sample_rate = audio_codec_context->sample_rate;

    struct SwrContext *audioSwrCtx = swr_alloc();
    audioSwrCtx = swr_alloc_set_opts(
        audioSwrCtx, out_channel_layout, out_sample_fmt, out_sample_rate,
        in_channel_layout, in_sample_fmt, in_sample_rate, 0, nullptr);
    swr_init(audioSwrCtx);

    int ret;

    while (true) {
        if (player_state != AVPlayer::State::PLAYING) {
            continue;
        }

        AVPacket pkt;
        if (!PopAudioPacket(pkt)) {
            continue;
        }

        avcodec_send_packet(audio_codec_context, &pkt);
        while (true) {
            ret = avcodec_receive_frame(audio_codec_context, in_frame);
            if (ret >= 0) {
                swr_convert(audioSwrCtx, &out_buffer, out_buffer_size,
                            (const uint8_t **)in_frame->data,
                            in_frame->nb_samples);

                while (audio_len > 0) { // Wait until finish
                    SDL_Delay(1);
                }

                // Set audio buffer (PCM data)
                // Audio buffer length
                audio_len = out_buffer_size;
                audio_pos = (Uint8 *)out_buffer;

                // std::this_thread::sleep_for(std::chrono::milliseconds(1));
            } else {
                break;
            }
            av_packet_unref(&pkt);
        }
    }
    swr_free(&audioSwrCtx);
    av_frame_free(&in_frame);
}

int AVPlayer::initSDL(void *winId) {
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        LOGE("SDL_Init error");
        return -1;
    }

    if (!winId) {
        std::string name = "AVPlayer " + std::to_string(sdl_window_width) +
                           "*" + std::to_string(sdl_window_height);
        // 创建SDL窗口
        if (!(sdl_window = SDL_CreateWindow(
                  name.data(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                  sdl_window_width, sdl_window_height,
                  SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE))) {
            LOGE("SDL_CreateWindow error");
            return -1;
        }
    } else {
        if (!(sdl_window = SDL_CreateWindowFrom(winId))) {
            LOGE("SDL_CreateWindow error");
            return -1;
        }
    }

    // 创建渲染器
    if (!(sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0))) {
        LOGE("SDL_CreateRenderer error");
        return -1;
    }

    // 创建纹理
    if (!(sdl_texture = SDL_CreateTexture(
              sdl_renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,
              sdl_window_width, sdl_window_height))) {
        LOGE("SDL_CreateTexture error");
        return -1;
    }

    // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");

    return 0;
}

bool AVPlayer::PushVideoPacket(const AVPacket &pkt) {

    if (av_packet_make_refcounted((AVPacket *)&pkt) < 0) {

        return false;
    }

    video_mutex.lock();
    video_packet_list.push_back(pkt);
    video_mutex.unlock();
    return true;
}

bool AVPlayer::PopVideoPacket(AVPacket &pkt) {
    std::lock_guard<std::mutex> lock(video_mutex);
    if (video_packet_list.empty()) {
        return false;
    } else {
        pkt = video_packet_list.front();
        video_packet_list.pop_front();
        return true;
    }
}

void AVPlayer::ClearVideoPkt() {

    video_mutex.lock();

    for (AVPacket pkt : video_packet_list) {
        av_packet_unref(&pkt);
    }
    video_packet_list.clear();

    video_mutex.unlock();
}

bool AVPlayer::PushAudioPacket(const AVPacket &pkt) {

    if (av_packet_make_refcounted((AVPacket *)&pkt) < 0) {
        return false;
    }

    audio_mutex.lock();
    audio_packet_list.push_back(pkt);
    audio_mutex.unlock();
    return true;
}

bool AVPlayer::PopAudioPacket(AVPacket &pkt) {
    std::lock_guard<std::mutex> lock(audio_mutex);
    if (audio_packet_list.empty()) {
        return false;
    } else {
        pkt = audio_packet_list.front();
        audio_packet_list.pop_front();
        return true;
    }
}

void AVPlayer::ClearAudioPkt() {

    audio_mutex.lock();

    for (AVPacket pkt : audio_packet_list) {
        av_packet_unref(&pkt);
    }
    audio_packet_list.clear();

    audio_mutex.unlock();
}

void AVPlayer::Read() {
    constexpr int MAX_AUDIO_SIZE = 50 * 20;
    constexpr int MAX_VIDEO_SIZE = 25 * 20;
    player_state = AVPlayer::State::PLAYING;
    AVPacket pkt;
    while (true) {
        if (player_state != AVPlayer::State::PLAYING) {
            continue;
        }

        if (av_read_frame(format_context, &pkt) >= 0) {
            if (video_index == pkt.stream_index) {
                PushVideoPacket(pkt);
            } else if (audio_index == pkt.stream_index) {
                PushAudioPacket(pkt);
            } else {
                av_packet_unref(&pkt);
                read_eof = true;
            }
        }

        // 这里做了个限制  当队列里面的数据超过某个大小的时候 就暂停读取
        // 防止一下子就把视频读完了，导致的空间分配不足
        // 这个值可以稍微写大一些
        if (video_packet_list.size() > MAX_VIDEO_SIZE ||
            audio_packet_list.size() > MAX_AUDIO_SIZE) {
            // Sleep(10);
            std::this_thread::sleep_for(std::chrono::seconds(1));
            // std::this_thread::sleep_for(std::chrono::milliseconds(10));
            continue;
        }
    }
}

void AVPlayer::ChangeState(State newState) {
    std::lock_guard<std::mutex> lock(state_mutex);
    player_state = newState;
}
